#include <stdbool.h>
#include <ctl_api.h>
#include <intrinsics.h>

#include "utils/crc32.h"
#include "utils/rtea/rtea.h"
#include "fatfs/ff.h"
#include "fatfs/mci.h"
#include "./drivers/wdt.h"
#include "./drivers/gsm/gsm.h"
#include "./drivers/iap/iap.h"
#include "./drivers/iap/sectors.h"
#include "./drivers/gsm/gsmio.h"
#include "./config/config.h"
#include "inithard.h"


const struct{
const unsigned char shw[16];//=" HW_Version>";
const unsigned int  HwID;//  = HARDWARE_ID;
const unsigned char duid[16];//="Device UID>";
const unsigned int  DvID;//  = DEVICE_ID;
}HWDVID={
// 1234567890123456
  "here HW_version>",HARDWARE_ID,
  "here Device UID>",DEVICE_ID
};

const unsigned char DefSett[]="START,0,1,5,1,9600,fixedip.nw,fixedip.nw,89.110.55.250,89.110.55.250,5004,5004,8888,192.168.3.172,192.168.3.182,255.255.255.0,192.168.3.1,5004,80\r\n";

void __putchar(char x)
{
}

void __assert(const char *expr, const char *file, int line)
{
}

void ctl_handle_error(CTL_ERROR_CODE_t e)
{
}


#define DEFAULT_APP_ADDR  0x10000
#define FIRMWARE_FILENAME "firmware.bin"
#define IAP_BUF_SIZE  1024
#define STACKSIZE 512

typedef void (*PFV)();

static FATFS fatfs;
static FATFS *pfs;  // нужен для вызова функции проверки FAT
static CTL_TASK_t   loader, worker;
static unsigned int worker_s[1+STACKSIZE+1];
static unsigned char buffer[IAP_BUF_SIZE] = "";
static unsigned char CfgBuf[512]; // буфер для конфигурации bootloader
static unsigned char SetBuf[512]; // буфер для конфигурации основной прошивки


//Config_t  *CONFIG;
Config_t  Gcfg;         // конфигурация для соединения

IVTSector *StoredIVT;   // сохраненный сектор с векторами прерываний
IVTSector NewIVT;       // новый сектор

bool app_upload(char* filename, unsigned int crc32);
void app_run(unsigned char remap);  // переход на 0. Если remap!=0 то подставляются сохраненные вектора, иначе просто перезапуск бутлоадера

bool crc_f(char* filename, unsigned int* crc, unsigned int* fsize);
void crc_m(unsigned int *crc, unsigned int size);

HTTP_GET_RESULT http_download(GSM_SIM_t sim, unsigned int *crc32, unsigned char *APN);
void  FormatSD(void);


//8**************************************************************************************
//
void worker_proc(void* state)
{
  HTTP_GET_RESULT res;
  led_red_off();
  led_yellow_off();
  led_green_off();
  
  unsigned int dcrc = 0;
  unsigned int mcrc = Gcfg.Crc32;
  unsigned int fcrc = 0;
  unsigned int fsize = 0;

  WDT_RESET();

  if((StoredIVT->sign!=IVT_STORE_SIGN)||(Gcfg.Update) ){// если стартуем первый раз или разрешен абдейт прошивки
    // в основной прошивке SIM1 и SIM2 переставлены, сначала пробуем для загрузки ту SIM что первая в основной прошивке
    res=http_download(GSM_SIM2, &dcrc,Gcfg.Network.Sim[0].APN);
    if (res==HTTP_ERROR) {  // не подключились к серверу, переход на другую SIM
      //debug_printf("failed!");
      WDT_RESET();
      res=http_download(GSM_SIM1, &dcrc,Gcfg.Network.Sim[1].APN);
    }
  }

  if(res==HTTP_FORMAT_REQUEST){  // пришел запрос на форматирование SD
    FormatSD();
    app_run(0);  // после форматирование уходим на перезагрузку
  }

  if (!crc_f(FIRMWARE_FILENAME, &fcrc, &fsize)) { // контроль CRC прошивки на SD-карте
    app_run(1);  // если не смогли прочитать прошивку то выполняем основной код
    return;
  }

  if (mcrc == fcrc){  // если CRC32 файла на SD сошелся с CRC32 из конфига то тоже выполняю код
    app_run(1);
    return;
  }

  if( fsize == 0 || dcrc != fcrc) { // если какой то трабл - CRC принятой прошивки не совпал с CRC файла на SD (а должен был совпасть)
    app_run(1);  // то тоже выполняется основной код
    return;
  }

  for (int i = 0; i < 10; i++){ // абдейт прошивки, 10 попыток
    if (app_upload(FIRMWARE_FILENAME,fcrc)) {// если абдейт удачен
/*        CONFIG->Crc32 = fcrc;         // то абдейт конфига // теперь это делается при записи таблицы векторов, конфиг ReadOnly
        CONFIG->Hardware = HARDWARE_ID;
        CONFIG->DeviceId = DEVICE_ID;
        config_save_eeprom();*/
        app_run(1);                    // и снова переход на выполнение кода
        return;
    };
  }

  app_run(1);  // выполнение кода при сбое абдейта прошивки
  return;
}

unsigned int  cycle;
volatile FRESULT   fsret;  // результат поднятия файловой системы

//8**************************************************************************************
//8**************************************************************************************
//8**************************************************************************************
//
int main(void)
{
//  PINSEL10=0; // отключил TRACE на P2.x
  PortInit();
  USART1DeInit();
//  InitEMC();
  StoredIVT=(IVTSector *)IVT_STORE_ADDR; 
  led_enable();
  led_red_on();
  led_yellow_on();
  led_green_on();
  led_red_off();
  led_yellow_off();
  led_green_off();
//  GSMIO_POWER_ON();
//  GSMIO_POWER_OFF();

  MODEM_CLR_RTS();

/*
  led_green_on();
  while(1){
    GSMIO_POWER_OFF();
    for(cycle=0;cycle<1000000;cycle++){
      led_red_off();
      led_red_on();
    }
    GSMIO_POWER_ON();
    for(cycle=0;cycle<1000000;cycle++){
      led_red_off();
      led_red_on();
    }
  }
  GSMIO_SET_KEY();
  GSMIO_SIM(GSM_SIM1);
  GSMIO_POWER_ON();
  GSMIO_POWER_OFF();
  GSMIO_SIM(GSM_SIM2);
  GSMIO_POWER_ON();
  led_green_on();
  led_green_off();
*/
  config_init(&Gcfg);
  if(StoredIVT->sign==IVT_STORE_SIGN){// было предыдущее сохранение, правильная CRC
    Gcfg.Crc32=StoredIVT->crc32;
  }else{
    Gcfg.Crc32=0;
  }
  Gcfg.Hardware=HWDVID.HwID;  // основное место хранения HW_Ver и Dev_UID - код бутлоадера, отсюда они копируются в флеш программ для доступа к ним основного кода
  Gcfg.DeviceId=HWDVID.DvID;

  /* Enable watchdog */
  WDT_MODE_RESET();
  WDT_ENABLE(0x7fffff8); //1 minute timeout

  /* Initialize stacks */
  memset(worker_s, 0xcd, sizeof(worker_s));  
  worker_s[0] = worker_s[1+STACKSIZE] = 0xfacefeed; 
/*
  disk_initialize(0);
  MCI_SendCmd(32,0,EXPECT_SHORT_RESP,0);
  MCI_GetCmdResp(0,EXPECT_SHORT_RESP,&cycle);
  MCI_SendCmd(33,100*1024,EXPECT_SHORT_RESP,0);
  MCI_GetCmdResp(0,EXPECT_SHORT_RESP,&cycle);
  MCI_SendCmd(38,0,EXPECT_SHORT_RESP,0);
  MCI_GetCmdResp(0,EXPECT_SHORT_RESP,&cycle);
  delay_ms(500);
*/
//  fsret=f_mount(0, &fatfs);
//  fsret=f_mkfs(0,1,64*512);   // форматирование
  

  ctl_task_init(&loader, 255, "Loader"); 
  ctl_start_timer(ctl_increment_tick_from_isr);
  ctl_global_interrupts_enable();
  fsret=f_mount(0, &fatfs);

//  fsret=f_mkfs(0,1,64*512);
//  pfs=&fatfs;
//FRESULT chk_mounted (	/* FR_OK(0): successful, !=0: any error occurred */
//	const TCHAR **path,	/* Pointer to pointer to the path name (drive number) */
//	FATFS **rfs,		/* Pointer to pointer to the found file system object */
//	BYTE chk_wp			/* !=0: Check media write protection for write access */
//)
//  fsret=chk_mounted("0:", &pfs, 0); // вызов внутренней функции FatFS для контроля SD


  // читаем правильную конфигурацию
  if(!config_load(&Gcfg,buffer,IAP_BUF_SIZE)){// сначала читаю из файла на SD
    //fsret=f_mkfs(0,1,64*512); // форматирование
    // если в файле ошибка, то смотрю во флэш
    if(StoredIVT->sign==IVT_STORE_SIGN){// если флеш правильная, то беру оттуда
        memcpy(&Gcfg, &(StoredIVT->cfg), sizeof(Config_t));
    }else config_init(&Gcfg);// если нет, то беру жестко прошитую конфигурацию
    StoreConfig(&Gcfg, CfgBuf, 512);
  }
  
   ctl_task_run(&worker, 1, worker_proc, 0, "Worker", STACKSIZE, worker_s + 1, 0);
  
  ctl_task_set_priority(&loader, 0); // drop to lowest priority to start created tasks running.

  while(1);
}

//8**************************************************************************************
//
bool crc_f(char* filename, unsigned int* crc, unsigned int* fsize)
{
  FIL file;
  if (f_open(&file, filename, FA_OPEN_EXISTING | FA_READ) == FR_OK) {
    *fsize = file.fsize;
    *crc = 0xFFFFFFFF;
    while (! f_eof(&file)) {
      unsigned int n = 0;
      char c = 0;
      f_read(&file, &c, 1, &n);
      *crc = Crc32Step(c, *crc);
      if (n != 1) break;
    }
    *crc = *crc ^ 0xFFFFFFFF;
    f_close(&file);
    return true;
  }
  return false;
}

//8**************************************************************************************
//
void crc_m(unsigned int *crc, unsigned int size)
{
  *crc = 0xFFFFFFFF;
  unsigned char* c = (unsigned char*)DEFAULT_APP_ADDR;
  for (unsigned int i = 0; i < size; i++)
  {
    *crc = Crc32Step(c[i], *crc);
  }
  *crc = *crc ^ 0xFFFFFFFF;
}

void led_yellow_blink()
{
  static bool on = false;
  on = !on;
  if (on) led_yellow_on();
  else    led_yellow_off();
}

//8**************************************************************************************
//

bool app_upload(char* filename, unsigned int crc32){
//  прошивка приходит в виде бинарника, в котором первые 64 байта - таблица прерываний, которую надо мапить
//  далее нули до 0x10000, затем сам код
//  сохраняю первые 64 байта в секторе 0x1B по адресу 0x0007D000
// остальную прошивку с адреса в файле 0x10000 пишу на адрес 0x10000 во флеш
  WDT_RESET();

  unsigned int sector_index;
  unsigned int addr;
  unsigned int bytes_to_read;
  unsigned long sector_size;   // индекс сектора во flash проца
  unsigned int br = 0;

  FIL file;
  if (f_open(&file, filename, FA_OPEN_EXISTING | FA_READ) != FR_OK)
    return false;

// сюда вставка записи первых 64 байт и переход в файле на 0x10000
  addr=IVT_STORE_ADDR;
  sector_index=getSectorIndex(addr);
  if (sector_index == INVALID_RESULT)
    return false;
  bytes_to_read = file.fsize;
  sector_size  = (getSectorSize(sector_index) << 10);
  
  if (iap_erase(sector_index)) return false;  // сначала стер сектор под вектора
  // читаю из файла первый сектор
  if (f_read(&file, &NewIVT, min(IAP_BUF_SIZE, sector_size), &br) != FR_OK)
    return false;
  rtea_decrypt((unsigned int*)&NewIVT, br/4, BOOT_KEY);
  // прописываю параметры
  NewIVT.crc32=crc32;
  NewIVT.HwID=HWDVID.HwID;  // основное место хранения HW_Ver и Dev_UID - код бутлоадера, отсюда они копируются в флеш программ для доступа к ним основного кода
  NewIVT.DvID=HWDVID.DvID;
  NewIVT.sign=IVT_STORE_SIGN;
  // копирование данных конфигурации успешного абдейта
  memcpy(&(NewIVT.cfg), &Gcfg, sizeof(Config_t));

  for(br=0;br<IVT_SECTOR_DATA_LEN;br++)NewIVT.data[br]=0xFFFFFFFF;  // забиваю остаток
  if (iap_program(sector_index, addr, (char *)&NewIVT, IAP_BUF_SIZE) != 0) // записали прерывания
    return false;

  // переход в файле
  f_lseek(&file,DEFAULT_APP_ADDR);
// продолжение того, что было

  addr=DEFAULT_APP_ADDR;
  sector_index = getSectorIndex(DEFAULT_APP_ADDR);
  if (sector_index == INVALID_RESULT)
    return false;

  addr = DEFAULT_APP_ADDR;
  bytes_to_read = file.fsize;
  sector_size  = (getSectorSize(sector_index) << 10);   // индекс сектора во flash проца

  if (iap_erase(sector_index)) return false;

  while (bytes_to_read) {
    WDT_RESET();
    led_yellow_blink();
    if (f_read(&file, &buffer, min(IAP_BUF_SIZE, bytes_to_read), &br) != FR_OK)
      return false;
    rtea_decrypt((unsigned int*)buffer, br/4, BOOT_KEY);
    if (iap_program(sector_index, addr, buffer, IAP_BUF_SIZE) != 0)
      return false;

    addr += IAP_BUF_SIZE;

    bytes_to_read -= br;
    sector_size   -= br;
    if (sector_size <= 0){
      sector_index += 1;
      sector_size  = (getSectorSize(sector_index) << 10);
      if (iap_erase(sector_index))
        return false;
    }
  }
  f_close(&file);
  return true;
}
//8**************************************************************************************
//
//
//
void app_run(unsigned char remap){
  unsigned int  *mem;
  unsigned int  *ivt;
  unsigned int  i,j;
  WDT_RESET();
  led_red_on();
  led_yellow_on();
  led_green_on();
  USART1DeInit();
  MCI_PowerDown();
  GSMIO_POWER_OFF();
  delay_ms(300);
  PortDeInit();
  delay_ms(100);
  WDT_RESET();
  ctl_global_interrupts_disable();
  __disable_interrupt();
  VICIntEnClr=0xFFFFFFFF;
  //VICINTENCLEAR=0xFFFFFFFF;
  mem=(unsigned int *)0x40000000; // адрес начала ОЗУ
  ivt=(unsigned int *)IVT_STORE_ADDR;// адрес сохраненных векторов
  if( (StoredIVT->sign==IVT_STORE_SIGN) && (remap) ){// если есть сигнатура записи то делаю ремап для старта основной прошивки
    for(i=0;i<IVT_STORE_SIZEW;i++)mem[i]=ivt[i];  // копирую
    MEMMAP=2; // ремап векторов на начало ОЗУ 
//  PFV fp = (PFV)(DEFAULT_APP_ADDR);
  }else{
    // если основной прошивки нет то просто мигаю всеми светодиодами и ухожу на сброс бутлоадера
    PortInit();
    for(j=0;j<4;j++){
      WDT_RESET();
      for(i=0;i<500000;i++){
        led_red_off();
        led_yellow_off();
        led_green_off();
      }
      for(i=0;i<500000;i++){
        led_red_on();
        led_yellow_on();
        led_green_on();
      }
    }
  }
  WDT_RESET();
  PFV fp = (PFV)(0x0);
//  for (i = 0x1000; i ; i--);  //
//  __asm__ ("ldr pc,=0");
  (*fp)(); 
  return;
}

//8**************************************************************************************
//
HTTP_GET_RESULT http_download(GSM_SIM_t sim, unsigned int *crc32, unsigned char *APN){
  HTTP_GET_RESULT result = HTTP_ERROR;
  GSM_STATUS_t status = GSM_INACTIVE;
  WDT_RESET();
//  status = gsm_enable(CONFIG->Network.Sim[sim].APN, sim, CONFIG->Network.Baud);
  MODEM_CLR_RTS();
  status = gsm_enable(APN, sim, BOOTMODEM_CONNECT_BAUD);
  //if (status == GSM_ERROR) debug_printf("ENABLE ERROR!\r\n");
  //else debug_printf("ENABLE OK!\r\n");
  if (status == GSM_DISCONNECTED) {
    WDT_RESET();
//    status = gsm_connect(CONFIG->Network.Sim[sim].Server, CONFIG->Network.Sim[sim].HttpPort);
    led_red_on();
    status = gsm_connect(Gcfg.Network.Sim[sim].Server, Gcfg.Network.Sim[sim].HttpPort,Gcfg.Network.TcpTimeout);
    //debug_printf("CONNECT OK!\r\n");
    if (status == GSM_CONNECTED) {
      char url[64] = "";
      snprintf(url, 64, "/api/firmware?id=%d&crc=%u", Gcfg.DeviceId, Gcfg.Crc32);
      //debug_printf("%s\r\n", url);
      WDT_RESET();
      led_yellow_on();
      status = gsm_http_download(Gcfg.Network.Sim[sim].Server, url, "0:firmware.bin", crc32, 120000);
      led_yellow_off();
      //debug_printf("INNER DONWLOAD OK!\r\n");
      if (status == GSM_CONNECTED) {
        result = HTTP_FILE_LOAD;
      }
      if(status == GSM_FORMAT_COMMAND)result=HTTP_FORMAT_REQUEST;
    }
  }
  led_red_off();
  WDT_RESET();
  gsm_disconnect();
  WDT_RESET();
  gsm_disable();
  WDT_RESET();
  return result;
}

//8*******************************************************************************
// форматирование SD
// читает bootconf.txt, ARCHIV8\SETTINGS\settings.bin
// форматирует SD
// сохраняет обратно bootconf.txt, ARCHIV8\SETTINGS\settings.bin
// !!! предварительно должна быть выполнена команда f_mount !!!
void  FormatSD(void){
  FIL file;
  unsigned int  bcfglen=0;  // длина файла конфигурации boot
  unsigned int  settlen=0;  // длина settings.bin
  unsigned int  br=0;
  led_green_on();
  if (f_open(&file, CONFIG_FILE, FA_OPEN_EXISTING | FA_READ) == FR_OK){  // читаю файл конфига bootloader-f
    if(file.fsize>512)br=512;
    else br=file.fsize;
    f_read(&file, CfgBuf, br, &bcfglen);// прочитал в буфер
    if(br!=bcfglen)bcfglen=0;
    f_close(&file);
  }
  if(f_chdir(SETTINGS_DIR0)==FR_OK){
    if(f_chdir(SETTINGS_DIR1)==FR_OK){
      if (f_open(&file, SETTINGS_FILE, FA_OPEN_EXISTING | FA_READ) == FR_OK){  // читаю файл конфига bootloader-f
        if(file.fsize>512)br=512;
        else br=file.fsize;
        f_read(&file, CfgBuf, br, &settlen);// прочитал в буфер
        if(br!=bcfglen)settlen=0;
        f_close(&file);
      }
    }
  }
/*
  MCI_SendCmd(32,0,EXPECT_SHORT_RESP,0);
  MCI_SendCmd(33,4000000,EXPECT_SHORT_RESP,0);
  MCI_SendCmd(38,0,EXPECT_SHORT_RESP,0);
  delay_ms(500);
*/
  for(br=0;br<10;br++){
    fsret=f_mkfs(0,1,64*512);   // форматирование
    if(fsret==FR_OK)br=100;
  }


  if(fsret==FR_OK){//если отформатировалось, то запись обратно файлов конфигурации
    if(bcfglen){// если файл конфигурации был прочитан
      if (f_open(&file, CONFIG_FILE, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK){
        f_write(&file,CfgBuf,bcfglen,&br);
        f_sync(&file);
        f_close(&file);
      }
    }else{// иначе конфиг либо из флеша либо из программы, считанный ранее
      StoreConfig(&Gcfg,CfgBuf,512);
    }
    delay_ms(50);

    // создаю конфигурацию для основной прошивки
    // сначала катологи
    if(f_mkdir(SETTINGS_DIR0)==FR_OK){
      delay_ms(50);
      f_chdir(SETTINGS_DIR0);
      if(f_mkdir(SETTINGS_DIR1)==FR_OK){
        delay_ms(50);
        f_chdir(SETTINGS_DIR1);
        if (f_open(&file, SETTINGS_FILE, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK){// создал файл установок
          if(settlen){// смотрю что в него записывать - считанное или жестко прошитое
            f_write(&file,SetBuf,settlen,&br);
          }else{
            f_write(&file,DefSett,sizeof(DefSett)-1,&br);
          }
          f_sync(&file);
          f_close(&file);
        }
      } 
    }
  }
  delay_ms(100);
  led_green_off();
  delay_ms(100);
  led_green_on();
  delay_ms(100);
  led_green_off();
  WDT_RESET();

}